package com.imeem.api.services;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Vector;

abstract class CoreService
{

  private final MessageDigest _md5;
  private final String _apiKey;
  private final String _apiSecret;
  protected CoreService(String apiKey, String apiSecret) throws NoSuchAlgorithmException
  {
    _apiKey = apiKey;
    _apiSecret = apiSecret;
    
    _md5 = MessageDigest.getInstance("MD5");
    _md5.reset();
  }
  
  private String buildSignature(String methodName, Hashtable<String,String> params) throws UnsupportedEncodingException
  {
    // sort the keys because they need to appear in alphabetical order
    Vector<String> v = new Vector<String>(params.keySet());
    Collections.sort(v);
    
    // first put the name of the method 
    StringBuffer buf = new StringBuffer();
    buf.append(methodName);
    
    // concatenate all they parameters in key=value form
    for (String key:v)
    {
      buf.append(key);
      buf.append('=');
      buf.append(params.get(key));
    }
    
    // append the secret token
    buf.append(_apiSecret);
    String stringToHash = buf.toString();
    _md5.update(stringToHash.getBytes("iso-8859-1"), 0, stringToHash.length());

    byte[] hash = _md5.digest();
    _md5.reset();
    
    buf = new StringBuffer();
    for (int i = 0; i < hash.length; i++)
    {
      int halfbyte = (hash[i] >>> 4) & 0x0F;
      int two_halfs = 0;
      do
      {
        if ((0 <= halfbyte) && (halfbyte <= 9))
          buf.append((char) ('0' + halfbyte));
        else
          buf.append((char) ('a' + (halfbyte - 10)));
        
        halfbyte = hash[i] & 0x0F;
      } while(two_halfs++ < 1);
    }
    return buf.toString();

  }

  /**
   * Builds a new hash table of the base set of parameters that all web methods 
   * need. This includes the API Key and Version. When you are done filling out 
   * the rest of your parameters, be sure to get the signature. 
   * @return the hash table of parameters
   */
  protected Hashtable<String,String> getBaseParameters()
  {
    Hashtable<String,String> result = new Hashtable<String,String>();
    
    result.put("apiKey", _apiKey);
    result.put("version", "1.0");
    
    return result;
  }
  
  protected InputStream invoke(String methodName, Hashtable<String,String> params) throws IOException
  {
    StringBuilder path = new StringBuilder(128);
    path.append("/api/xml/");
    path.append(methodName);
    if (!params.isEmpty())
    {
      path.append('?');
      for (String key:params.keySet())
      {
        path.append(key);
        path.append('=');
        path.append(URLEncoder.encode(params.get(key).trim(), "UTF-8"));
        path.append('&');
      }
    }
    path.append("sig=");
    path.append(this.buildSignature(methodName, params));
    
    URL url = new URL("http", "www.api.imeem.com", path.toString());
    return url.openConnection().getInputStream();
  }
}
